﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using Raven.Client;
using Raven.Client.Linq;
using PrairieAsunder.Models;
using System.Threading.Tasks;

namespace PrairieAsunder.Common
{
    /// <summary>
    /// Syncs the songs on disk with the songs in the database.
    /// </summary>
    public class SongsSync
    {
        public Task Sync()
        {
            // Asynchronously find new songs on disk and delete missing ones from the database.
            var findNewSongs = Task.Factory.StartNew(FindNewSongsOnDisk);
            var deleteMissingSongs = findNewSongs.ContinueWith(_ => RemoveSongsDeletedFromDisk());
            return deleteMissingSongs;
        }

        private void FindNewSongsOnDisk()
        {
            var songsOnDiskChunks = Directory
                .GetFiles(Constants.MusicDirectory, "*.mp3")
                .Select(Path.GetFileName)
                .Chunk(25); 

            foreach (var songChunk in songsOnDiskChunks)
            {
                using (var session = Get.A<IDocumentStore>().OpenSession())
                {
                    foreach (var fileName in songChunk)
                    {
                        var songInDb = session
                            .Query<Song>()
                            .FirstOrDefault(s => s.FileName == fileName);
                        if (songInDb == null)
                        {
                            session.Store(new Song(fileName));                            
                        }
                    }

                    session.SaveChanges();
                }
            }

#if DEBUG
            // Give Raven a chance to read the writes when booting in debug mode.
            System.Threading.Thread.Sleep(250);
#endif
        }

        private void RemoveSongsDeletedFromDisk()
        {
            var songsOnDisk = Directory
                .EnumerateFiles(Constants.MusicDirectory, "*.mp3")
                .Select(Path.GetFileName)
                .ToArray();

            var skip = 0;
            var take = 25;
            while (true)
            {
                using (var session = Get.A<IDocumentStore>().OpenSession())
                {
                    var songs = session
                        .Query<Song>()
                        .Skip(skip)
                        .Take(take)
                        .ToArray();

                    // Delete the likes.
                    var songIds = songs.Select(s => s.Id).ToArray();
                    var deleteLikesCommands = session.Query<Like>()
                        .Where(l => l.SongId.In(songIds))
                        .AsEnumerable()
                        .Select(l => new Raven.Abstractions.Commands.DeleteCommandData { Key = "likes-" + l.Id.ToString() })
                        .AsEnumerable<Raven.Abstractions.Commands.ICommandData>();
                    
                    // Delete the song.
                    var deleteSongCommands = songs
                        .Where(s => !songsOnDisk.Contains(s.FileName, StringComparer.InvariantCultureIgnoreCase))
                        .Select(s => new Raven.Abstractions.Commands.DeleteCommandData { Key = "songs-" + s.Id.ToString() })
                        .AsEnumerable<Raven.Abstractions.Commands.ICommandData>()
                        .Concat(deleteLikesCommands);

                    session.Advanced.Defer(deleteLikesCommands
                        .Concat(deleteSongCommands)
                        .ToArray());
                    session.SaveChanges();

                    if (songs.Length == 0)
                    {
                        break;
                    }
                    else
                    {
                        skip += songs.Length;
                    }
                }
            }
        }
    }
}